package com.hd.hdxgddcommon.utils;

import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用于读取Excel表格的实体类
 */
public class ExcelReader extends ReflectBase {

    private Workbook wb;

    /**
     * 映射excel中的表头和实体类的属性
     */
    private Map<String, String> excelNameMap;

    /**
     * excel中的表头
     */
    private String[] title;


    /**
     * excel中的表头对应的实体类的属性名，如果没有注入，则为null
     */
    private String[] entryName;

    private ExcelReader(InputStream is) throws IOException, InvalidFormatException {
        wb = WorkbookFactory.create(is);
    }

    public static ExcelReader getDefaultExcelReader(InputStream is) throws IOException, InvalidFormatException {
        ExcelReader excelReader = new ExcelReader(is);
        excelReader.setTitle(excelReader.readExcelTitle());
        return excelReader;
    }


    public Map<String, String> getExcelNameMap() {
        return excelNameMap;
    }

    /**
     * 如果传入的map不为空，则按照map 的映射关系生成entryName的对应数组
     *
     * @param excelNameMap
     */
    public void setExcelNameMap(Map<String, String> excelNameMap) {
        if (excelNameMap != null && !excelNameMap.isEmpty()) {
            this.excelNameMap = excelNameMap;
            int len = title.length;
            this.entryName = new String[len];
            for (int i = 0; i < len; i++)
                this.entryName[i] = excelNameMap.get(title[i]);
        }
    }

    public String[] getTitle() {
        return title;
    }

    private void setTitle(String[] title) {
        this.title = title;
    }


    /**
     * excel对应的对象
     * timestamp 对应 yyyy-MM-dd hh:mm:ss
     * date 对应 yyyy-MM-dd
     *
     * @param clazz
     * @param <V>
     * @return
     * @throws Exception
     */
    public <V> List<V> readAsEntry(Class<V> clazz) throws Exception {
        Field[] fields = clazz.getDeclaredFields();
        return readExcelContent().stream().map(entry -> {
            //通过反射实例化Class<V>
            final Object[] v = new Object[1];
            try {
                v[0] = clazz.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            //遍历clazz的所有字段并赋值
            Arrays.stream(fields).forEach(field -> {
                String fieldName = field.getName();
                String fieldValue = entry.get(fieldName);

                if (fieldValue != null && !fieldValue.isEmpty()) {
                    Class<?> type = field.getType();
                    Method method = null;
                    try {
                        method = clazz.getMethod(fieldNameToMethodName(SET, fieldName), type);
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                    String typeName = type.getSimpleName();

                    Object value = null;
                    if (typeName.equals("Integer")) {
                        int indexOf = fieldValue.indexOf('.');
                        fieldValue = fieldValue.substring(0, indexOf);
                        value = Integer.parseInt(fieldValue);

                    } else if (typeName.equals("Boolean")) {
                        value = Boolean.parseBoolean(fieldValue);
                    } else if (typeName.equals("Double")) {
                        value = Double.parseDouble(fieldValue);
                    } else if (typeName.equals("String")) {
                        value = fieldValue;
                    } else if (typeName.equals("Date")) {
                        try {
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                            value = sdf.parse(fieldValue);
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }
                    } else if (typeName.equals("Timestamp")) {
                        value = Timestamp.valueOf(fieldValue);
                    }
                    try {
                        method.invoke(v[0], value);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            });

            return (V) v[0];
        }).collect(Collectors.toList());
    }


    /**
     * 读取Excel表格表头的内容
     *
     * @return String 表头内容的数组
     */
    public String[]
    readExcelTitle() {
        Sheet sheet = wb.getSheetAt(0);
        Row row = sheet.getRow(0);
        // 标题总列数
        int colNum = row.getPhysicalNumberOfCells();
        String[] title = new String[colNum];
        for (int i = 0; i < colNum; i++) {
            Cell cell = row.getCell(i);
            title[i] = getCellFormatValue(cell);
        }
        return title;
    }

    /**
     * 读取Excel数据内容
     *
     * @return Map 包含单元格数据内容的list<Map>对象
     */
    public List<Map<String, String>> readExcelContent() {
        String[] s = entryName == null ? title : entryName;
        List<Map<String, String>> list = new ArrayList<>();
        Sheet sheet = wb.getSheetAt(0);
        // 得到总行数
        int rowNum = sheet.getLastRowNum();
        Row row = sheet.getRow(0);
        int colNum = row.getPhysicalNumberOfCells();
        // 正文内容应该从第二行开始,第一行为表头的标题
        for (int i = 1; i <= rowNum; i++) {
            Map<String, String> map = new HashMap<>();
            row = sheet.getRow(i);
            int j = 0;
            int k = 0;
            while (j < colNum) {
                // 每个单元格的数据内容用"-"分割开，以后需要时用String类的replace()方法还原数据
                // 也可以将每个单元格的数据设置到一个javabean的属性中，此时需要新建一个javabean
                // str += getStringCellValue(row.getCell((short) j)).trim() +
                // "-";
                String c = getCellFormatValue(row.getCell(j)).trim();
                if (StringUtils.isBlank(c)) k++;
                map.put(s[j++], c);
            }
            if (k != j)//全空的行就不加进去了
                list.add(map);

        }
        return list;
    }

    /**
     * 根据Cell类型设置数据
     */
    private String getCellFormatValue(Cell cell) {
        String cellValue;
        if (cell != null) {
            // 判断当前Cell的Type
            switch (cell.getCellType()) {
                // 如果当前Cell的Type为NUMERIC
                case Cell.CELL_TYPE_NUMERIC:
                case Cell.CELL_TYPE_FORMULA: {
                    // 判断当前的cell是否为Date
                    if (HSSFDateUtil.isCellDateFormatted(cell)) {
                        // 如果是Date类型 则转化为Data格式

                        //方法1：这样子的data格式是带时分秒的：2011-10-12 0:00:00
                        //cellValue = cell.getDateCellValue().toLocaleString();

                        //方法2：这样子的data格式是不带时分秒的：2011-10-12
                        Date date = cell.getDateCellValue();
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                        cellValue = sdf.format(date);

                    }
                    // 如果是纯数字
                    else {
                        // 取得当前Cell的数值
                        cellValue = String.valueOf(cell.getNumericCellValue());
                    }
                    break;
                }
                // 如果当前Cell的Type为STRIN
                case Cell.CELL_TYPE_STRING:
                    // 取得当前的Cell字符串
                    cellValue = cell.getRichStringCellValue().getString();
                    break;
                // 默认的Cell值
                default:
                    cellValue = " ";
            }
        } else {
            cellValue = "";
        }
        return cellValue;

    }

}